(function(){
    var extend = function(target, source){ for (key in source) target[key] = source[key]; return target; }
    
    var pxStyle = ['left', 'right', 'top', 'bottom', 'width', 'height'];
    
    ['Left', 'Right', 'Top', 'Bottom'].forEach(function(p){pxStyle.push('margin'+p);pxStyle.push('padding'+p);});
    
    var css = function(prop, val) {
        if (val === undefined) {
            return (pxStyle.indexOf(prop) != -1) ? parseInt(this.style[prop] || 0) : this.style[prop];
        } else {
            this.style[prop] = val + ((pxStyle.indexOf(prop) != -1) ? 'px' : '');
        }
        return this;
    };
    
    var pos = function() {
        var el = this;
        var p = [el.offsetLeft, el.offsetTop];
        while (el = el.parentNode) {
            p[0] += el.offsetLeft || 0;
            p[1] += el.offsetTop || 0;
        }
        return p;
    }
    
    var attr = function(name, val) {
        if (val === undefined) {
            return this.getAttribute(name);
        } else if (!val) {
            this.removeAttribute(name);
        } else {
            this.setAttribute(name, val);
        }
    }

    var _$ = {
      css: css,
      addEvent: function(event, func) {this.addEventListener(event, func, false); return this;},
      pos: pos,
      attr: attr
    };
    
    window.$ = function(name, node) {
        if (name instanceof Node) return extend(name, _$);
        if (/#[a-z0-9_]+$/i.test(name)) {
            var obj = (node||document).querySelector(name);
            return obj ? extend(obj, _$) : null;
        } else {
            return [].slice.call((node||document).querySelectorAll(name)).map(function(el){
                return extend(el, _$);
            });
        }
    };
    
    $.ajax = function (url, callback) {
      var req = new XMLHttpRequest();
      req.onreadystatechange = function() {
        if (req.readyState == 4) {
          callback(req.responseText);
        }
      };
      req.open("GET", url, true);
      req.send("");
    };
    
    $.ce = function (html, attrs, styles) {
        var el = $(document.createElement('div'));
        el.innerHTML = html;
        for (var i in (attrs||{})) {
            if (i == 'className') el.className = attrs[i];
            else el.setAttribute(i, attrs[i]);
        }
        for (var i in (styles||{})) {
            el.css(i, styles[i]);
        }
        return el;
    };

    window.Book = function(id, obj) {
        var def = {
            id: id || 0,
            author: '',
            title: '',
            cover: '../img/bookshell/'+(id||0)+'.jpg',
            state: Book.STATE_NEW,
            shell: 0,
            bookcase: 0,
            side: 0,
            page: 0,
            order: 0,
            div: null
        };
        extend(this, def);
        extend(this, obj);
    };
    
    extend(Book.prototype, {
        getElement: function() {
            if (!this.div) this.div = $.ce('<div class="book_wrap" title="'+this.title+'"><div class="delete" nodrag="1">&times;</div><div class="cover dragview" style="background-image:url(img/bookshell/book'+this.id+'.jpg)"></div></div>', {id: 'book'+this.id, className:'book', getdrag:1});
            return this.div;
        },
        getData: function() {
            var data = {}, self = this;
            ['author', 'title', 'cover', 'state', 'shell', 'bookcase', 'side', 'page', 'order'].forEach(function(prop){
                data[prop] = self[prop];
            });
            return data;
        },
        onStartDrag: function() {
            var div = this.getElement();
            if (this.state == Book.STATE_BOOKCASE) {
                div.css('display', 'none');
            }
        },
        onEndDrag: function() {
            //console.log('end drag');
            var div = this.getElement();
            if (this.state == Book.STATE_BOOKCASE) {
                div.css('display', 'block');
            }
        }
    });
    
    extend(Book, {
        STATE_NEW: 0,
        STATE_BOOKCASE: 1,
        STATE_REMOVED: 2
    });    
    
    window.Bookcase = function(id, obj) {
        var def = {
            id: id || 0,
            title: '',
            div: null,
            shells: [],
            books: [],
            /*hiddenShell: null,*/
            navUp: null,
            navDown: null,
            shellShift: 0,
            newShellAdded: false
        };
        extend(this, def);
        extend(this, obj);

    };
    
    Bookcase.SHELL_HEIGHT = 197;
    Bookcase.MIN_SHELL_COUNT = 3;
    Bookcase.maxWindowCount = 0;

    extend(Bookcase.prototype, {
        init: function() {
            var self = this;
            var div = this.getElement();

            //this.hiddenShell = this.addShell(true);
            this.navUp = $('.nav_up', div)[0];
            this.navDown = $('.nav_down', div)[0];
            this.addBtn = $('.add_button', div)[0];
            
            var i = 0;
            var wh = window.innerHeight;
            var maxCount = parseInt(wh / Bookcase.SHELL_HEIGHT);
            Bookcase.maxWindowCount = maxCount;
            
            if (maxCount < Bookcase.MIN_SHELL_COUNT) maxCount = Bookcase.MIN_SHELL_COUNT;
            while (i < maxCount) {
                h = Bookcase.SHELL_HEIGHT * i;
                this.addShell();
                //this.minShellCount++;
                //console.log(h, wh);
                i++;
            }
            
            this.navUp.addEvent('click', function(e) {
                if (self.shellShift == 0) return;
                self.shellShift--;
                var shells = $('.bookshells', self.getElement())[0];
                shells.css('marginTop', -self.shellShift * Bookcase.SHELL_HEIGHT);
                if (self.shellShift == 0) {
                    self.navUp.css('opacity', 0);
                    setTimeout(function(){
                        self.navUp.css('visibility', 'hidden');
                    }, 300);
                    /*
                    self.addBtn.css('visibility', 'visible');
                    self.addBtn.css('opacity', 0.4);
                    */
                }
                if (self.shellShift < self.shells.length - Bookcase.maxWindowCount) {
                    self.navDown.css('visibility', 'visible');
                    self.navDown.css('opacity', 0.4);
                }
            });
            
            this.navDown.addEvent('click', function(e) {
                var delta = self.shells.length - Bookcase.maxWindowCount;
                if (self.shellShift >= delta) return;
                console.log(self.shells.length, Bookcase.maxWindowCount, self.shellShift);
                self.shellShift++;
                self.navUp.css('visibility', 'visible');
                self.navUp.css('opacity', 0.4);
                var shells = $('.bookshells', self.getElement())[0];
                shells.css('marginTop', -self.shellShift * Bookcase.SHELL_HEIGHT);
                if (self.shellShift == delta) {
                    self.navDown.css('opacity', 0);
                    setTimeout(function() {
                        self.navDown.css('visibility', 'hidden');
                    }, 300);
                }
            });
            
            DragAndDrop.addListener(this.addBtn, {
                onEnter: function() {
                    if (self.newShellAdded) return;
                    self.newShellAdded = true;
                    self.addShell(true);
                }
            });
            
            DragAndDrop.addListener(div, {
                onEnter: function() {
                    console.log('enter');
                    if (self.shellShift == 0) {
                        self.addBtn.css('visibility', 'visible');
                        self.addBtn.css('opacity', 0.4);
                    }
                },
                onLeave: function() {
                    self.addBtn.css('opacity', 0);
                    setTimeout(function() {
                        self.addBtn.css('visibility', 'hidden');
                    }, 300);
                },
                onEnd: function() {
                    self.addBtn.css('opacity', 0);
                    setTimeout(function() {
                        self.addBtn.css('visibility', 'hidden');
                    }, 300);
                }
            })
        },
        getElement: function() {
            if (!this.div) this.div = $.ce('<div class="header"><div class="title">'+this.title+'</div></div><div class="bookcase_add_shell"></div><div class="bookcase_nav"><div class="add_button">+</div><div class="nav_up">&uarr;</div><div class="nav_down">&darr;</div></div><div class="bookcase_back"><div class="bookshells"></div></div>', {id: 'bookcase'+this.id, className:'bookcase', getdrag: 1});
            return this.div;
        },
        getData: function() {
            var data = {}, self = this;
            ['id', 'title'].forEach(function(prop) {
                data[prop] = self[prop];
            });
            return data;
        },
        addShell: function(smooth) {
            var div = $('.bookshells', this.getElement())[0];
            var self = this;
            
            var len = this.shells.length;
            var shellID = /*hidden ? 'bookshell'+this.id+'_hidden' :*/ 'bookshell'+this.id+'_'+len;
            var shell = $.ce('<div class="books"></div><div class="plank"></div>', {id: shellID, className: smooth ? 'bookshell hide' : 'bookshell'});
            
            if (this.shells.length > 0) {
                div.insertBefore(shell, this.shells[this.shells.length - 1]);
            } else {
                div.appendChild(shell);
            }
            this.shells.push(shell);

            if (smooth) {
                setTimeout(function() {
                    shell.className = "bookshell";
                }, 10);
            }

            if (len >= Bookcase.maxWindowCount) {
                console.log('show')
                this.navDown.css('visibility', 'visible');
                this.navDown.css('opacity', 0.4);
            }
            
            var droppingPreview = $.ce('<div class="book_preview_back"></div>', {className:'book_preview', getdrag:1});
            $('.books', shell)[0].appendChild(droppingPreview);
            
            var onEnd = function(book) {
                if (self.getBookCount(this) >= 5 && self.shells.indexOf(this) != book.shell) return false;
                self.newShellAdded = false;
                //console.log('drop', book);
                droppingPreview.css('display', 'none');
                if (!book) return;
                
                var div = book.getElement();

                book.shell = self.shells.indexOf(this);
                
                if (book.state == Book.STATE_NEW) {
                    BookcaseManager.selector.remove(book);
                } else if (book.state == Book.STATE_REMOVED) {
                    BookcaseManager.trashBin.remove(book);
                } else if (book.state == Book.STATE_BOOKCASE) {
                    BookcaseManager.bookcases.remove(book);
                }
                self.add(book, this, droppingPreview);
                
                book.bookcase = self.id;
                book.state = Book.STATE_BOOKCASE;
                BookcaseManager.db.updateBook(book);
            }
            
            DragAndDrop.addListener(shell, {
                onStart: function(book) {
                    var el = book.getElement();
                    droppingPreview.parentNode.removeChild(droppingPreview);
                    el.parentNode.insertBefore(droppingPreview, el.nextSibling);
                    droppingPreview.css('display', 'block');
                },
                onEnter: function(book) {
                    if (self.getBookCount(this) >= 5) return false;
                    droppingPreview.css('display', 'block');
                },
                onLeave: function() {
                    //if (self.getBookCount(this) >= 5) return false;
                    droppingPreview.css('display', 'none');
                },
                onMove: function(book, x, y, target) {
                    //console.log(book.shell);
                    if (self.getBookCount(this) >= 5 && self.shells.indexOf(this) != book.shell) return false;
                    if (target && target.className == 'book_preview') return;
                    var p = droppingPreview.parentNode;
                    if (target == null) {
                        if (p.lastChild != droppingPreview) {
                            p.removeChild(droppingPreview);
                            p.appendChild(droppingPreview);
                        }
                    } else {
                        if (target.compareDocumentPosition(droppingPreview) & 2) { // target follows droppingPreview
                            p.removeChild(droppingPreview);
                            if (target.nextSibling) {
                                p.insertBefore(droppingPreview, target.nextSibling);
                            } else {
                                p.appendChild(droppingPreview);
                            }
                        } else {
                            p.removeChild(droppingPreview);
                            p.insertBefore(droppingPreview, target);
                        }
                    }
                },
                onEnd: onEnd
            });
            
            return shell;
        },
        removeShell: function(shell) {
            shell.parentNode.removeChild(shell);
            this.shells.splice(this.shells.indexOf(shell), 1);
        },
        getBookCount: function(shell) {
          return $('.book', shell).length;  
        },
        add: function(book, shell, next) {
            var self = this;
            
            this.books.push(book);
            
            var addOrdered = function(booksDiv) {
                var found = false;
                for (var i = 0, l = self.books.length; i < l; i++) {
                    var b = self.books[i];
                    if (b.shell == book.shell) {
                        console.log(b.order, book.order);
                    }
                    if (b.shell == book.shell && b.order > book.order) {
                        booksDiv.insertBefore(book.div, b.div);
                        found = true;
                        break;
                    }
                }
                if (!found) booksDiv.appendChild(book.div);
            };
            
            var el = book.getElement();
            if (shell === undefined) {
                if (this.shells.length <= book.shell) {
                    for (var i = 0, l = this.shells.length; i < book.shell - l + 1; i++) {
                        this.addShell();
                    }
                }
                shell = this.shells[book.shell];

                var booksDiv = $('.books', shell)[0];
                if (next) {
                    booksDiv.insertBefore(el, next);
                    this.reorder(shell);
                } else {
                    addOrdered(booksDiv);
                }
            } else {
                var booksDiv = $('.books', shell)[0];
                if (next) {
                    booksDiv.insertBefore(el, next);
                    this.reorder(shell);
                } else {
                    addOrdered(booksDiv);
                }
            }
        },
        reorder: function(shell) {
            var bookDivs = $('.book', shell);
            this.books.forEach(function(book) {
                var idx = bookDivs.indexOf(book.getElement());
//                console.log(bookDivs);
                if (idx != -1 && idx != book.order) {
                    book.order = idx;
                    console.log('update order', idx);
                    BookcaseManager.db.updateBook(book);
                }
            });
        },
        remove: function(book) {
            
            this.books.splice(this.books.indexOf(book), 1);
            var div = book.getElement();
            var booksDiv = div.parentNode;
            booksDiv.removeChild(div);
            var shift = 0;
            for (var i = 0, l = this.shells.length; i < l - Bookcase.MIN_SHELL_COUNT; i++) {
                var shell = this.shells[i];
                if (this.getBookCount(shell) == 0) {
                    shift++;
                    //console.log(shell);
                    this.removeShell(shell);
                } else {
                    break;
                }
            }
            //console.log(shift);
            if (shift) {
                for (var i = 0, l = this.shells.length; i < l; i++) {
                    this.shells[i].id = 'bookshell' + this.id + '_' + i;
                }
                for (var i = 0, l = this.books.length; i < l; i++) {
                    this.books[i].shell -= shift;
                    BookcaseManager.db.updateBook(this.books[i]);
                }
            }
        }
    });

})();


var Selector = function() {
    var wrap = $('#books_select_wrap');
    var container = $('#books_select');
    var switcher = $('#books_select_show');
    var bodyWidth = document.body.offsetWidth;

    wrap.css('left', -bodyWidth + 40);

    wrap.css('width', bodyWidth);
    container.css('width', bodyWidth - 60);
    
    this.add = function(book) {
        container.appendChild(book.getElement());
    }
    
    this.remove = function(book) {
        container.removeChild(book.getElement());
    }
    
    switcher.addEvent('click', function(){
        var shown = wrap.attr('shown') ? 0 : 1;
        wrap.attr('shown', shown);
        wrap.css('left', shown ? 0 : -bodyWidth + 40);
    });

};

var Bookcases = function() {
    var container = $('#bookcases');
    var bodyWidth = parseInt(document.body.offsetWidth);
    container.css('width', bodyWidth);
    var bookcases = {};
    var current = 0;
    var count = 0;
    
    this.addBookcase = function(bookcase) {
        bookcase.init();
        bookcases[bookcase.id] = bookcase;
        container.css('width', container.css('width') + bodyWidth);
        var div = bookcase.getElement();

        div.css('marginLeft', (bodyWidth - 800) / 2);
        div.css('marginRight', (bodyWidth - 800) / 2);
        container.appendChild(div);
        count++;
    };
    
    this.add = function(book) {
        bookcases[book.bookcase].add(book);
    };

    this.remove = function(book) {
        bookcases[book.bookcase].remove(book);
    };
    
    this.scroll = function(direction) {
        var left = container.css('left');
        if (current + direction < 0 || current + direction >= count) return;
        current += direction;
        container.css('left', left - direction * bodyWidth);
        $('#nav_left').css('opacity', current == 0 ? 0 : 0.3);
        $('#nav_right').css('opacity', current >= count - 1 ? 0 : 0.3);
    }
};

var DragAndDrop = {
    target: null,
    targetObject: null,
    preview: null,
    listenerObjects: [],
    listeners: [],
    objects: [],
    area: null,
    init: function() {
        var self = this;
        preview = $('#drag_container');
        
        var delay = 0;
        
        var onDown = function(e) {
            delay = 0;
            var el = e.target;
            if (el.getAttribute('nodrag')) return;
            while (el = el.parentNode) {
                if (el.getAttribute('nodrag')) return;
                if (el == document.body) break;
                self.objects.forEach(function(arr) {
                    if (arr[0] == el) {
                        self.target = el;
                        self.targetObject = arr[1];
                        //console.log('set target', arr[1]);
                        if (self.targetObject.onStartDrag) self.targetObject.onStartDrag.call(self.targetObject);
                    }
                });
                if (self.target) {
                    break;
                }
            }
            if (self.target) {
                preview.innerHTML = $('.dragview', self.target)[0].outerHTML;
                preview.css('left', e.clientX);
                preview.css('top', e.clientY);
                while (el = el.parentNode) {
                    if (el == document.body) break;
                    var i = self.listenerObjects.indexOf(el);
                    if (i != -1) {
                        //console.log('down', el, self.listenerObjects);
                        var funcs = self.listeners[i];
                        if (funcs.onStart) funcs.onStart.call(el, self.targetObject);
                        self.area = el;
                        if (funcs.onEnter) funcs.onEnter.call(el, self.targetObject);
                        //break;
                    }
                }
            }
        };
        
        document.addEventListener('mousedown', function(e) {
            delay = setTimeout(function(){return onDown(e);}, 200);
        });
        
        document.addEventListener('mousemove', function(e) {
            if (!self.target) return;
            preview.css('left', e.clientX);
            preview.css('top', e.clientY);
            preview.css('display', 'none');
            var trg = document.elementFromPoint(e.clientX, e.clientY);
            preview.css('display', 'block');
            e.preventDefault();
            
            var el = trg;
            var getDragEl = null;
            if (el.getAttribute('getdrag')) getDragEl = el;
            var area = null;
            var callEnter = false;
            while (el) {
                if (el == document.body) break;
                if (el.getAttribute('getdrag')) getDragEl = el;
                var i = self.listenerObjects.indexOf(el);

                if (i != -1) {
                    // console.log('area', self.listenerObjects);
                    if (!area) area = el;
                    var funcs = self.listeners[i];
                    if (self.area != area) {
                        if (self.area !== null) {
                            var old = self.listenerObjects.indexOf(self.area);
                            
                            console.log(self.area, area, self.area.compareDocumentPosition(area));

                            if (!(self.area.compareDocumentPosition(area) & 16)) { // self.area not contains area
                                if (self.listeners[old].onLeave) self.listeners[old].onLeave.call(self.area);
                            }
                        }
                        self.area = area;
                        callEnter = true;
                    }
                    
                    if (callEnter) {
                        if (funcs.onEnter) {
                            funcs.onEnter.call(area, self.targetObject);
                        }
                    }
                    
                    if (funcs.onMove) {
                        funcs.onMove.call(area, self.targetObject, e.clientX, e.clientY, getDragEl);
                    }
                    // break;
                }
                el = el.parentNode;
            }
            if (area == null && self.area !== null) {
                var el = self.area;
                while (el) {
                    var old = self.listenerObjects.indexOf(el);
                    if ((old != -1) && self.listeners[old].onLeave) {
                        self.listeners[old].onLeave.call(el);
                    }
                    el = el.parentNode;
                }
                self.area = null;
            }
        });
        
        var fixScroll = function() {
            document.body.scrollTop = 0;
            document.body.scrollLeft = 0;
        }
        document.addEventListener('scroll', fixScroll);
        fixScroll();
        
        document.addEventListener('mouseup', function(e) {
            clearTimeout(delay);
            delay = 0;
            if (!self.target) return;
            e.preventDefault();
            preview.css('left', 0);
            preview.css('top', 0);
            preview.innerHTML = '';
            var trg = document.elementFromPoint(e.clientX, e.clientY);
            
            var el = trg;
            var currentObjects = [];
            while (el = el.parentNode) {
                if (el == document.body) break;
                var i = self.listenerObjects.indexOf(el);
                if (i != -1) {
                    currentObjects.push(i);
                    if (self.listeners[i].onEnd) {
                        self.listeners[i].onEnd.call(el, self.targetObject);
                    }
                    //break;
                }
            }
            self.listeners.forEach(function(listener, i) {
                if ((currentObjects.indexOf(i) == -1) && self.listeners[i].onLeave) {
                    self.listeners[i].onLeave.call(self.listenerObjects[i], self.targetObject);
                }
            });
            if (self.targetObject.onEndDrag) self.targetObject.onEndDrag.call(self.targetObject);
            self.area = null;
            self.target = null;
            self.targetObject = null;
            //console.log('delete target');
        });
    },
    addListener: function(el, funcs) {
        this.listenerObjects.push(el);
        this.listeners.push(funcs);
    },
    register: function(el, obj) {
        this.objects.push([el, obj]);
    }
}

var BookcaseManager = {
    selector: null,
    trashBin: null,
    bookcases: null,
    bookViewer: null,
    db: null,
    init: function() {
        var bm = BookcaseManager;
        bm.selector = new Selector();
        bm.trashBin = new TrashBin();
        bm.bookcases = new Bookcases();
        bm.bookViewer = new BookViewer();
        
        var navOffset = (window.innerHeight - 400) / 2;
        $('#nav_left').css('top', navOffset);
        $('#nav_right').css('top', navOffset);
        
        
        
        bm.db = new BookshellDB(function(){
            bm.db.getBooks(function(bookcases, books){
                bm.initData(bookcases, books);
            });
        });
    },
    initData: function(bookcasesData, booksData) {
        
        var db = BookcaseManager.db;
        
        var bookViewer = BookcaseManager.bookViewer;
        var bookcases = BookcaseManager.bookcases;
        var selector = BookcaseManager.selector;
        var trashBin = BookcaseManager.trashBin;
        
        var navTO = 0;
        
        var onNavEnterFunc = function(direction) {
            return function() {
                clearTimeout(navTO);
                navTO = setInterval(function(){
                    bookcases.scroll(direction);
                }, 1000);
                bookcases.scroll(direction);
            }
        };
        
        var onNavLeave = function() {
            clearTimeout(navTO);
        }
        
        DragAndDrop.addListener($('#nav_left'), {
            onEnter: onNavEnterFunc(-1),
            onLeave: onNavLeave
        });
        
        DragAndDrop.addListener($('#nav_right'), {
            onEnter: onNavEnterFunc(1),
            onLeave: onNavLeave
        });
    
        $('#nav_left').addEvent('click', function() {
            clearTimeout(navTO);
            bookcases.scroll(-1);
        });
    
        $('#nav_right').addEvent('click', function() {
            clearTimeout(navTO);
            bookcases.scroll(1);
        });
        
        for(var i in bookcasesData) {
            bookcases.addBookcase(bookcasesData[i]);
        }
        
        for(var i in booksData) {
            var book = booksData[i];
            
            if (book.state == Book.STATE_NEW) {
                selector.add(book);
            } else if (book.state == Book.STATE_BOOKCASE) {
                bookcases.add(book);
            } else {
                //console.log('add to bin');
                trashBin.add(book, true);
            }
            
            var div = book.getElement();
            DragAndDrop.register(div, book);
            
            $('.cover', div)[0].addEvent('click', (function(_book){ return function(e) {
                if (_book.state == Book.STATE_BOOKCASE) {
                    bookViewer.show(_book);
                }
            }})(book));
                
            $('.delete', div)[0].addEvent('click', (function(_book){ return function(e){
              var b = $(this.parentNode.parentNode);
    
              b.css('left', b.offsetLeft);
              b.css('top', b.offsetTop);
    
              setTimeout(function(){
                b.setAttribute('deleted', 1);
                b.css('left', window.innerWidth - 180);
                b.css('top', window.innerHeight - 180);
                b.css('opacity', 0);
    
                setTimeout(function(){
                  b.parentNode.removeChild(b);
                  b.removeAttribute('deleted');
                  trashBin.add(_book);
                  b.css('opacity', 1);
                  _book.state = Book.STATE_REMOVED;
                  db.updateBook(_book);
                }, 500);
              }, 0);
            }})(book));
        };
    }
}

var TrashBin = function() {
  var container = $('#trash_bin');
  var booksDiv = $('#trash_bin_books');
  var wrap = $('#trash_bin_wrap');
  
  var img = new Image();
  img.src = 'img/bookshell/bin_full.png';
  
  var trashedBooks = [];
  
  container.addEvent('click', function() {
    if (trashedBooks.length > 0) wrap.attr('opened', wrap.attr('opened')? 0 : 1);
  });
  
  this.add = function(book, noBounce) {
    if (!noBounce) container.attr('bounce', 1);
    var cell = $.ce('', {className:'trash_bin_books_cell'});
    cell.appendChild(book.getElement());
    booksDiv.appendChild(cell);
    trashedBooks.push(book);
    container.css('backgroundImage', 'url(img/bookshell/bin_full.png)');
    setTimeout(function(){container.attr('bounce', null);}, 500);
  }
  
  this.remove = function(book) {
    trashedBooks.forEach(function(_book, i){
        if (_book.id == book.id) {
            var cell = _book.div.parentNode;
            booksDiv.removeChild(cell);
            trashedBooks.splice(i, 1);
        }
    });
    //console.log(trashedBooks);
    if (trashedBooks.length == 0) {
        container.css('backgroundImage', 'url(img/bookshell/bin_empty.png)');
        wrap.attr('opened', 0);
    }
  }
};

var BookViewer = function() {
    var self = this;
    var prevPage = $('#book_viewer_back_page');
    var nextPage = $('#book_viewer_pages');
    var canvas = $('#book_viewer_canvas');
    var viewer = $('#book_viewer');
    var back = $('#book_viewer_back');
    //var pageNumber = $('#book_viewer_page_number');
    
    var renderInt = 0;
    
    var currentBook;

    back.addEvent('click', function(){
        //console.log('click back');
        self.hide();
    });
    
    this.hide = function() {
        //viewer.css('display', 'none');
        back.css('opacity', 0.01);
        viewer.css('visibility', 'hidden');
        if (renderInt) { clearInterval(renderInt); renderInt = 0; }
   }

    var pages;
    var mouse = {x:0, y:0}, flips = [], page = 0;
    var pageWidth = nextPage.offsetWidth;
    var prevWidth = prevPage.offsetWidth;
    var canvasPadding = 100;
    var pageHeight = 0;
    
    var init = function (book) {
        pages = $('section', viewer);
        page = book.page || 0;
        flips = [];
            
        pageHeight = window.innerHeight - 200;
        nextPage.css('height', pageHeight);
        prevPage.css('height', pageHeight);
        canvas.height = pageHeight + 2 * canvasPadding;
        
        //pageNumber.css('top', 100 + pageHeight - 50);
        //pageNumber.innerHTML = page + 1;
        
        for (var i = 0, l = pages.length; i < l; i++) {
            pages[i].css('zIndex', l - i);
            if (i < page) {
                pages[i].css('width', 0);
            } else {
                pages[i].css('width', 800);
            }
            flips.push( {
                progress: 1,
                target: 1,
                page: pages[i], 
                dragging: false
            } );
        }
        
        currentBook = book;
        //viewer.css('display', 'block');
        if (renderInt) { clearInterval(renderInt); renderInt = 0; }
        viewer.css('visibility', 'visible');
        back.css('opacity', 0.4);
        renderInt = setInterval( render, 1000 / 60 );
    };
   
    this.show = function(book) {
        
        //load pages
        //
        var onDone = function(text) {
            nextPage.innerHTML = text;
            init(book);
        };
        
        //$.ajax('http://kuzya.org/test.html', onDone);
        
        //TEMP: simulate ajax on local
        onDone($('#ajax_book'+book.id).innerHTML);
    }
    
    canvas.width = prevWidth + pageWidth;

    var context = canvas.getContext( "2d" );
    
    viewer.addEvent('mousemove', function(e){
        mouse.x = e.clientX - nextPage.offsetLeft;
        mouse.y = e.clientY - nextPage.offsetTop;
        moved = true;
    });
    
    var moved = false;
    
    viewer.addEvent('mousedown', function(e) {
        moved = false;
        if (Math.abs(mouse.x) < pageWidth) {
            //console.log(page, flips);
            if (mouse.x < 0 && page - 1 >= 0) {
                flips[page - 1].dragging = true;
            }
            else if (mouse.x > 0 && page + 1 < flips.length) {
                flips[page].dragging = true;
                //console.log('dragging true');
            }
        }
  
        e.preventDefault();
    });
  
    //var current = 0;
    viewer.addEvent('mouseup', function(e) {
        //console.log('viewer up');
        if (mouse.y < 100 || mouse.y > pageHeight - 100) {
            self.hide();
            return;
        }
        for( var i = 0, l = flips.length; i < l; i++ ) {
            if( flips[i].dragging ) {
                //console.log('mouse up');
                if( mouse.x < 0 == moved) {
                    flips[i].target = -1;
                    page = Math.min( i + 1, flips.length );
                }
                else {
                    flips[i].target = 1;
                    page = i;
                }
                //pageNumber.innerHTML = page + 1;
                currentBook.page = page;
                //console.log('update', page);
                BookcaseManager.db.updateBook(currentBook);
                //current = i;
            }
  
            flips[i].dragging = false;
        }
    });
    
    var render = function() {
        context.clearRect( 0, 0, canvas.width, canvas.height );
        
        for( var i = 0, len = flips.length; i < len; i++ ) {
            var flip = flips[i];
        
            if( flip.dragging ) {
              flip.target = Math.max( Math.min( mouse.x / pageWidth, 1 ), -1 );
            }
        
            flip.progress += ( flip.target - flip.progress ) * 0.3;
            
            if( flip.dragging || Math.abs( flip.progress ) < 0.997 ) {
              drawFlip( flip );
            }
        
        }
    }
    
    var drawFlip = function(flip) {
        var strength = 1 - Math.abs( flip.progress );
        
        var foldWidth = ( pageWidth * 0.5 ) * ( 1 - flip.progress );
        
        var foldX = pageWidth * flip.progress + foldWidth;
        
        var verticalOutdent = 20 * strength;
        
        var paperShadowWidth = ( pageWidth * 0.5 ) * Math.max( Math.min( 1 - flip.progress, 0.5 ), 0 );
        var rightShadowWidth = ( pageWidth * 0.5 ) * Math.max( Math.min( strength, 0.5 ), 0 );
        var leftShadowWidth = ( pageWidth * 0.5 ) * Math.max( Math.min( strength, 0.5 ), 0 );
        
        flip.page.css('width', Math.max(foldX, 0));
        
        var getGradient = function(x1, y1, x2, y2, colors) {
            var gradient = context.createLinearGradient(x1, y1, x2, y2);
            for (var i = 0, l = colors.length; i < l; i += 2) {
                gradient.addColorStop(colors[i], colors[i+1]);
            }
            return gradient;
        }
        
        context.save();
        context.translate( prevWidth, canvasPadding);
        
        context.strokeStyle = 'rgba(0,0,0,'+(0.05 * strength)+')';
        context.lineWidth = 30 * strength;
        context.beginPath();
        context.moveTo(foldX - foldWidth, -verticalOutdent * 0.5);
        context.lineTo(foldX - foldWidth, pageHeight + (verticalOutdent * 0.5));
        context.stroke();
        
        context.fillStyle = getGradient(foldX, 0, foldX + rightShadowWidth, 0,
                                        [0, 'rgba(0,0,0,'+(strength*0.2)+')',
                                         0.8, 'rgba(0,0,0,0.0)']);
        context.beginPath();
        context.moveTo(foldX, 0);
        context.lineTo(foldX + rightShadowWidth, 0);
        context.lineTo(foldX + rightShadowWidth, pageHeight);
        context.lineTo(foldX, pageHeight);
        context.fill();
        
        context.fillStyle = getGradient(foldX - foldWidth - leftShadowWidth, 0, foldX - foldWidth, 0,
                                        [0, 'rgba(0,0,0,0.0)',
                                         1, 'rgba(0,0,0,'+(strength*0.15)+')']);
        context.beginPath();
        context.moveTo(foldX - foldWidth - leftShadowWidth, 0);
        context.lineTo(foldX - foldWidth, 0);
        context.lineTo(foldX - foldWidth, pageHeight);
        context.lineTo(foldX - foldWidth - leftShadowWidth, pageHeight);
        context.fill();
        
        context.fillStyle = getGradient(foldX - paperShadowWidth, 0, foldX, 0,
                                        [0.35, '#fafafa',
                                         0.73, '#eeeeee',
                                         0.9, '#fafafa',
                                         1.0, '#e2e2e2']);
        context.strokeStyle = 'rgba(0,0,0,0.06)';
        context.lineWidth = 0.5;
        
        context.beginPath();
        context.moveTo(foldX, 0);
        context.lineTo(foldX, pageHeight);
        context.quadraticCurveTo(foldX, pageHeight + (verticalOutdent * 2), foldX - foldWidth, pageHeight + verticalOutdent);
        context.lineTo(foldX - foldWidth, -verticalOutdent);
        context.quadraticCurveTo(foldX, -verticalOutdent * 2, foldX, 0);
        
        context.fill();
        context.stroke();
        
        context.restore();
    }
    
};

var BookshellDB = function(onReady) {
    var dbReq = webkitIndexedDB.open('BooksDB', 'Books');
    var IDBTransation = webkitIDBTransaction;
    
    var version = 1.0;
    
    var db;
    
    dbReq.onsuccess = function(e) {
        db = e.target.result;
        
        var init = function() {
            var trans = db.transaction(['books', 'bookcases'], IDBTransation.READ_WRITE);

            var store = trans.objectStore('books');
        
            var books = [
                new Book(1, {author: "Илья Ильф", title: "Одноэтажная Америка"}),
                new Book(2, {author: "Джером Д. Селинджер", title: "Над пропастью во ржи"}),
                new Book(3, {author: "Хорхе Луис Борхес", title: "Рассказы"}),
                new Book(4, {author: "Умберто Эко", title: "Парижское кладбище"}),
                new Book(5, {author: "Умберто Эко", title: "Имя розы"}),
                new Book(6, {author: "Джеймс Джойс", title: "Улисс"})
            ];
            
            store.clear();

            books.forEach(function(book){
                var req = store.add(book.getData(), book.id);
            });

            store = trans.objectStore('bookcases');
        
            var bookcases = [
                new Bookcase(1, {title: "Классическая проза"}),
                new Bookcase(2, {title: "Современная проза"}),
                new Bookcase(3, {title: "Поэзия"})
            ];
            
            store.clear();

            bookcases.forEach(function(bookcase){
                var req = store.add(bookcase.getData(), bookcase.id);
            });
        };
        
        if (db.version != version) {
            
            var ver = db.setVersion(version);
            
            ver.onsuccess = function() {
                
                try {
                    db.deleteObjectStore('books');
                    db.deleteObjectStore('bookcases');
                } catch (e) {}
                var os = db.createObjectStore('books', 'id', true);
                
                ['state', 'bookcase', 'shell'].forEach(function(f){
                    os.createIndex(f, f, false);
                });

                var os = db.createObjectStore('bookcases', 'id', true);
                
                init();
                if (onReady) onReady();
            }
        } else {
            if (onReady) onReady();
        }
    };
    
    this.getBooks = function(onDone) {
        if (!db) {
            if (onDone) onDone({});
        }
        
        var books = {};
        var bookcases = {};
        
        var booksDone = false, bookcasesDone = false;
        
        var trans = db.transaction(['books', 'bookcases'], IDBTransation.READ_ONLY);
        
        var onSuccess = function ( e ) {
            var source = e.target.source;
            var cursor = e.target.result;

            if ( cursor ) {
                switch (source.name) {
                    case 'books': books[cursor.key] = new Book(cursor.key, cursor.value); break;
                    case 'bookcases': bookcases[cursor.key] = new Bookcase(cursor.key, cursor.value); break;
                }
                
                cursor.continue();
            } else {
                switch (source.name) {
                    case 'books': booksDone = true; break;
                    case 'bookcases': bookcasesDone = true; break;
                }
                if (onDone && booksDone && bookcasesDone) onDone(bookcases, books);
            }
        }
        
        var store = trans.objectStore('books');
        var readCursor = store.openCursor();
        readCursor.onsuccess = onSuccess;
        
        store = trans.objectStore('bookcases');
        readCursor = store.openCursor();
        readCursor.onsuccess = onSuccess;
    }
    
    this.updateBook = function(book) {
        var trans = db.transaction(['books'], IDBTransation.READ_WRITE);
        var store = trans.objectStore('books');
        
        store.put(book.getData(), book.id);
    }
};

document.addEventListener('DOMContentLoaded', function(){
    DragAndDrop.init();
    BookcaseManager.init();
});
